package io.israel.benjamin;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;

import java.util.EnumSet;

@SpringBootApplication
public class IsraelBenjaminApplication {


	public static void main(String[] args) {
		SpringApplication.run(IsraelBenjaminApplication.class, args);
	}

	@Bean
	public InitializingBean initializingBean(MyApp myApp) {
		return myApp::doSignals;
	}

	@Bean
	public StateMachine<States, Events> buildMachine() throws Exception {
		StateMachineBuilder.Builder<States, Events> builder = StateMachineBuilder.builder();

		builder.configureStates()
				.withStates()
				.initial(States.STATE1)
				.states(EnumSet.allOf(States.class));

		builder.configureTransitions()
				.withExternal()
				.source(States.STATE1).target(States.STATE2)
				.event(Events.EVENT1)
				.and()
				.withExternal()
				.source(States.STATE2).target(States.STATE1)
				.event(Events.EVENT2);

		return builder.build();
	}

	@Bean
	MyApp myApp(StateMachine<States, Events> stateMachine) {
		return new MyApp(stateMachine);
	}
}


@Slf4j
@WithStateMachine
class MyBean {

	@OnTransition(target = "STATE1")
	public void toState1() {
		log.info("TO STATE {}", 1);
	}

	@OnTransition(target = "STATE2")
	public void toState2() {
		log.info("TO STATE {}", 2);
	}
}

class MyApp {

	private StateMachine<States, Events> stateMachine;

	public MyApp(StateMachine<States, Events> stateMachine) {
		this.stateMachine = stateMachine;
	}

	void doSignals() {
		stateMachine.start();
		stateMachine.sendEvent(Events.EVENT1);
		stateMachine.sendEvent(Events.EVENT2);
	}
}

@Configuration
@EnableStateMachine
class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {

	@Override
	public void configure(StateMachineStateConfigurer<States, Events> states)
			throws Exception {
		states.withStates()
				.initial(States.STATE1)
				.states(EnumSet.allOf(States.class));
	}

	@Override
	public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
			throws Exception {
		transitions
				.withExternal()
				.source(States.STATE1).target(States.STATE2)
				.event(Events.EVENT1)
				.and()
				.withExternal()
				.source(States.STATE2).target(States.STATE1)
				.event(Events.EVENT2);
	}
}

enum States {
	STATE1, STATE2
}

enum Events {
	EVENT1, EVENT2
}